﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Serialization;

namespace UnityConfig
{
    public partial class Main : Form
    {
        public Main()
        {
            InitializeComponent();
        }

        private void btnSelect_Click(object sender, EventArgs e)
        {
            openDllFileDialog.Filter = "dll文件|*.dll";
            if (openDllFileDialog.ShowDialog() == DialogResult.OK)
            {
                txtPath.Text = openDllFileDialog.FileName;
            }
        }

        private void Anlize(string path, bool ignoreManyImpl = false, List<string> ignoreInterfaces = null)
        {
            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += ReflectionOnlyAssemblyResolve;
            var list = new List<Type>();
            string[] dllFiles = null;
            if (File.Exists(path))
            {
                if (!".dll".Equals(Path.GetExtension(path), StringComparison.OrdinalIgnoreCase))
                {
                    throw new Exception("必须是dll文件");
                }
                dllFiles = Directory.GetFiles(Path.GetDirectoryName(path), "*.dll");
                list.AddRange(GetInterfaces(path));
            }
            else if (Directory.Exists(path))
            {
                dllFiles = Directory.GetFiles(path, "*.dll");
                foreach (var item in dllFiles)
                {
                    list.AddRange(GetInterfaces(item));
                }
            }
            else
            {
                throw new Exception("文件或目录都不存在！");
            }
            if (ignoreInterfaces != null)
            {
                foreach (var ignoreInterface in ignoreInterfaces)
                {
                    foreach (var item in list.Where(p => Regex.IsMatch(GetClsName(p), ignoreInterface)).ToArray())
                    {
                        list.Remove(item);
                    }
                }
            }
            //  var dickData = list.ToDictionary(p => p, p => false);
            var dict = new Dictionary<Type, List<Type>>();
            foreach (var dllFile in dllFiles)
            {
                var assembly = Assembly.ReflectionOnlyLoadFrom(dllFile);
                var opt = new ParallelOptions()
                {
                    //MaxDegreeOfParallelism = 1
                };
                Parallel.ForEach(assembly.GetTypes(), opt, type =>
                {
                    //Debug.Assert(!type.FullName.Contains("BizActivity"), "BizActivity");
                    if (type.IsInterface)
                    {
                        return;
                    }
                    foreach (var result in list)
                    {
                        //if (type.FullName.Contains("BizActivity") && result.FullName.Contains("Activity"))
                        //{
                        //    Console.WriteLine("");
                        //}
                        if (result.IsAssignableFrom(type))
                        {
                            if (!dict.ContainsKey(result))
                            {
                                dict[result] = new List<Type>();
                            }
                            dict[result].Add(type);
                        }
                    }
                });
            }
            //  var sb = new StringBuilder();

            List<TypeAlias> typeAliases = new List<TypeAlias>();
            List<UnityMapType> mapTypes = new List<UnityMapType>();
            foreach (var item in dict)
            {
                if (ignoreManyImpl && item.Value.Count > 1)
                {
                    continue;
                }
                var interfaceType = item.Key;
                var interfaceCls = GetClsName(interfaceType);
                //   sb.Append(interfaceCls).AppendLine();
                typeAliases.Add(new TypeAlias()
                {
                    Type = interfaceCls,
                    Alias = interfaceType.Name
                });
                foreach (var implType in item.Value)
                {
                    var implCls = GetClsName(implType);
                    //    sb.Append("     ").AppendLine(implCls);
                    typeAliases.Add(new TypeAlias()
                    {
                        Type = implCls,
                        Alias = implType.Name
                    });
                    mapTypes.Add(new UnityMapType()
                    {
                        Type = interfaceType.Name,
                        MapTo = implType.Name,
                    });
                }
                //  sb.AppendLine();
            }
            //  File.WriteAllText(@"d:\log\UnityConfig.log", sb.ToString());
            var unity = new Unity()
            {
                TypeAliases = typeAliases,
                Containers = new List<Container>(){
                    new Container()
                {
                    Types=mapTypes
                }},
            };
            var configPath = Path.Combine(Path.GetTempPath(), "Unity.config");
            if (!ckbTipSaveFile.Checked)
            {
                saveConfigFileDialog.Filter = "配置文件|*.config|所有文件|*.*";
                if (saveConfigFileDialog.ShowDialog() != DialogResult.OK)
                {
                    return;
                }
                configPath = saveConfigFileDialog.FileName;
            }
            Serilaze(unity, configPath);
            Process.Start(configPath);
            //MessageBox.Show("ok");
        }

        protected string GetClsName(Type type)
        {
            return type.FullName + "," + type.Assembly.GetName().Name;
        }

        protected List<Type> GetInterfaces(string dllPath)
        {
            var assembly = Assembly.ReflectionOnlyLoadFrom(dllPath);
            var list = new List<Type>();
            Parallel.ForEach(assembly.GetTypes(), type =>
            {
                if (!type.IsInterface)
                {
                    //list.Add(">>>>"+type.FullName);
                    return;
                }
                list.Add(type);
                //list.Add(type.Assembly.GetName().Name + "," + type.FullName);
            });
            //  File.WriteAllLines(@"d:\log\UnityConfig.log", list);
            return list;
        }


        protected Assembly ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
        {
            var assemblyName = new AssemblyName(args.Name);
            var rootAssembly = args.RequestingAssembly.Location;
            String asmToCheck = Path.Combine(Path.GetDirectoryName(rootAssembly), assemblyName.Name + ".dll");
            if (File.Exists(asmToCheck))
            {
                return Assembly.ReflectionOnlyLoadFrom(asmToCheck);
            }

            return Assembly.ReflectionOnlyLoad(args.Name);
        }

        protected void Serilaze(object obj, string saveName)
        {
            XmlSerializer xmlSerializer = new XmlSerializer(obj.GetType());
            var xmlNameSpace = new XmlSerializerNamespaces();
            xmlNameSpace.Add(string.Empty, string.Empty);
            using (var file = File.Open(saveName, FileMode.Create))
            {
                xmlSerializer.Serialize(file, obj, xmlNameSpace);
            }
        }

        private void btnBuild_Click(object sender, EventArgs e)
        {
            var path = txtPath.Text.Trim();
            if (string.IsNullOrWhiteSpace(path))
            {
                txtPath.Focus();
                return;
            }
            List<string> ignoreConfigData = ReadIgnoreConfig(txtIgnoreConfig.Text.Trim());
            try
            {
                Anlize(path, ckbIgnoreManyImpl.Checked, ignoreConfigData);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "程序异常");
            }
        }

        protected List<string> ReadIgnoreConfig(string path)
        {
            var list = new List<string>();
            if (path.Length > 0)
            {
                if (!File.Exists(path))
                {
                    txtIgnoreConfig.Focus();
                    throw new Exception("忽略接口配置文件不存在！");
                }
            }
            else
            {
                return list;
            }
            foreach (var line in File.ReadAllLines(path))
            {
                var tmp = line.Trim();
                if (tmp.Length == 0 || tmp[0] == '#')
                {
                    continue;
                }
                list.Add(tmp);
            }
            return list;
        }

        private void btnCreateConfig_Click(object sender, EventArgs e)
        {
            const string ignoreText = @"#####################################################################
#将不想生成的接口放在此配置文件中，接口格式为：接口全名,程序集名称
#例：Erp.Interface.IActivity,Erp.Interface
#每一行写一个接口，行首有#的表示注释行，程序会忽略空行
#支持正则表达式，
#例：\w*Service,Erp.Interface$
#####################################################################

";
            saveIgnoreConfigFileDialog.Filter = "配置文件|*.ini|所有文件|*.*";
            if (saveIgnoreConfigFileDialog.ShowDialog() == DialogResult.OK)
            {
                if (txtIgnoreConfig.Text.Length==0)
                {
                    txtIgnoreConfig.Text = saveIgnoreConfigFileDialog.FileName;
                }
                File.WriteAllText(saveIgnoreConfigFileDialog.FileName, ignoreText);
                Process.Start(saveIgnoreConfigFileDialog.FileName);
            }
        }

        private void btnSelectIgnoreConfig_Click(object sender, EventArgs e)
        {
            openDllFileDialog.Filter = "配置文件|*.ini|所有文件|*.*";
            if (openDllFileDialog.ShowDialog() == DialogResult.OK)
            {
                txtIgnoreConfig.Text = openDllFileDialog.FileName;
            }
        }
    }
}
